using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Random = System.Random;

namespace Gj
{
    public static class UnityTools
    {
        public static List<UnityObject> SortObjects(List<UnityObject> objects, Vector3 position)
        {
            return objects.OrderBy(a => Vector3.Distance(position, a.GetPosition())).ToList();
        }

        public static Control GetControl(GameObject obj)
        {
            return obj.GetComponent<Control>();
        }

        public static T GetComponentRequire<T>(GameObject obj) where T : Component
        {
            var t = obj.GetComponent<T>();
            if (t == null) t = obj.AddComponent<T>();
            return t;
        }

        public static Component GetComponentRequire(Type type, GameObject obj)
        {
            var t = obj.GetComponent(type);
            if (t == null) t = obj.AddComponent(type);
            return t;
        }

        public static void Swap<T>(ref T a, ref T b)
        {
            T temp = a;
            a = b;
            b = temp;
        }

        public static void DataToTexture(int x, int y, byte[][] data, ref Texture2D tex, int size = 1)
        {
            var d = new byte[(x * size) * (y * size)];
            for (var _x = 0; _x < x; _x++)
            {
                for (int _y = 0; _y < y; _y++)
                {
                    for (var _xo = 0; _xo < size; _xo++)
                    {
                        for (int _yo = 0; _yo < size; _yo++)
                        {
                            d[x * size * (_y * size + _xo) + (_x * size + _yo)] = data[_x][_y];
                        }
                    }
                }
            }

            SetupTexture2D(x * size, y * size, ref tex, TextureFormat.Alpha8);
            tex.LoadRawTextureData(d);
            tex.Apply();
        }

        public static void ToTexture2D(RenderTexture rTex, ref Texture2D tex)
        {
            var t = DateTime.Now;
            SetupTexture2D(rTex.width, rTex.height, ref tex);
            // ReadPixels looks at the active RenderTexture.
            RenderTexture.active = rTex;
            tex.ReadPixels(new Rect(0, 0, rTex.width, rTex.height), 0, 0);
            tex.Apply();
        }

        public static void ToRenderTexture(Texture2D tex, ref RenderTexture rTex)
        {
            SetupRenderTexture(tex.width, tex.height, ref rTex);
            rTex.enableRandomWrite = true;
            RenderTexture.active = rTex;
            Graphics.Blit(tex, rTex);
        }

        public static void SetupRenderTexture(int x, int y, ref RenderTexture rTex, RenderTextureFormat type = RenderTextureFormat.ARGB32)
        {
            if (rTex == null)
                rTex = new RenderTexture(x, y, 0, type);
            else if (rTex.width != x || rTex.height != y)
            {
                rTex.width = x;
                rTex.height = y;
            }
        }

        public static void SetupTexture2D(int x, int y, ref Texture2D tex, TextureFormat type = TextureFormat.ARGB32)
        {
            if (tex == null || tex.width != x || tex.height != y)
                tex = new Texture2D(x, y, type, false);
        }
        
        public static Texture2D RenderTextureToTexture2D(RenderTexture renderTexture)
        {
            // 1. 临时保存当前活动的RenderTexture
            RenderTexture previousActive = RenderTexture.active;

            // 2. 设置目标RenderTexture为活动状态
            RenderTexture.active = renderTexture;

            // 3. 创建新的Texture2D（尺寸与RenderTexture一致）
            Texture2D texture2D = new Texture2D(
                renderTexture.width, 
                renderTexture.height   // 线性颜色空间（若项目使用HDR则为false）
            );

            // 4. 读取像素数据
            texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
            texture2D.Apply(); // 应用像素更改

            // 5. 恢复之前的活动RenderTexture
            RenderTexture.active = previousActive;

            return texture2D;
        }

        public static bool AllowStatus(Skill skill, Life target)
        {
            if (target == null) return false;
            switch (skill.targetStatus)
            {
                case TargetStatus.Live:
                    return target.IsLive();
                case TargetStatus.Die:
                    return !target.IsLive();
                default:
                    return true;
            }
        }

        public static float HexSpeed(float speed)
        {
            return 0.86f / speed;
        }

        public static bool AllowCast(Skill skill, Cast master, Life target)
        {
            if (skill.targetRelation == TargetRelation.None) return true;
            if (skill.targetRelation == TargetRelation.Self) return master == target;

            if (skill.targetRelation == TargetRelation.Partner)
                return master.GetData().IsPartner(target.GetData());
            if (skill.targetRelation == TargetRelation.Enemy)
                return master.GetData().IsEnemy(target.GetData());
            return false;
        }

        public static bool AllowCast(SkillData skill, EntityData master, EntityData target)
        {
            if (skill.targetRelation == TargetRelation.None) return true;
            if (skill.targetRelation == TargetRelation.Self) return master.id == target.id;

            if (skill.targetRelation == TargetRelation.Partner)
                return master.IsPartner(target);
            if (skill.targetRelation == TargetRelation.Enemy)
                return master.IsEnemy(target);
            return false;
        }

        public static bool AllowRange(Vector3 p1, Vector3 p2, float radius)
        {
            var distance = (float)Math.Round(Vector3.Distance(p1, p2), 2);
            return distance < radius;
        }

        public static bool AllowRange(Skill skill, Cast cast, Vector3 p)
        {
            var distance = (float)Math.Round(Vector3.Distance(cast.GetPosition(), p), 2);
            if (distance > cast.GetCastRadius()) return false;
            return distance <= skill.distance;
        }


        public static float ComputeRotation(float value)
        {
            if (value > 360)
                return value - 360;
            if (value < 0)
                return value + 360;
            return value;
        }

        public static Random GetRandom(int seed)
        {
            return new Random(seed);
        }

        public static bool IsOk(Random random, float ratio)
        {
            if (ratio > 0)
                if (random.Next(0, 100) < ratio)
                    return true;
            return false;
        }

        public static bool Point2Sphere(Vector3 sourcePosition, Vector3 targetPosition, float targetRadius)
        {
            return Vector3.Distance(sourcePosition, targetPosition) <= targetRadius;
        }

        public static bool Sphere2Sphere(Vector3 sourcePosition, float sourceRadius, Vector3 targetPosition,
            float targetRadius)
        {
            return Vector3.Distance(sourcePosition, targetPosition) <= sourceRadius + targetRadius;
        }

        public static bool Point2Box(Vector3 sourcePosition, Vector3 targetPosition, float targetSize)
        {
            var r = targetSize / 2;
            return sourcePosition.x >= targetPosition.x - r
                   && sourcePosition.x <= targetPosition.x + r
                   && sourcePosition.y >= targetPosition.y - r &&
                   sourcePosition.y <= targetPosition.y + r
                   && sourcePosition.z >= targetPosition.z - r &&
                   sourcePosition.z <= targetPosition.z + r;
        }

        public static bool Box2Box(Vector3 sourcePosition, float sourceSize, Vector3 targetPosition, float targetSize)
        {
            var r1 = sourceSize / 2;
            var r2 = targetSize / 2;
            return sourcePosition.x - r1 <= targetPosition.x + r2
                   && sourcePosition.x + r1 >= targetPosition.x - r2
                   && sourcePosition.y - r1 <= targetPosition.y + r2
                   && sourcePosition.y + r1 >= targetPosition.y - r2
                   && sourcePosition.z - r1 <= targetPosition.z + r2
                   && sourcePosition.z + r1 >= targetPosition.z - r2;
        }

        public static bool Sphere2Box(Vector3 sourcePosition, float sourceRadius, Vector3 targetPosition,
            float targetSize)
        {
            var r = targetSize / 2;
            var closest = new Vector3(
                Math.Max(targetPosition.x - r, Math.Min(sourcePosition.x, targetPosition.x + r)),
                Math.Max(targetPosition.y - r, Math.Min(sourcePosition.y, targetPosition.y + r)),
                Math.Max(targetPosition.z - r, Math.Min(sourcePosition.z, targetPosition.z + r))
            );
            return Vector3.Distance(closest, sourcePosition) <= sourceRadius;
        }

        /// <summary>
        ///     计算直线与平面的交点
        /// </summary>
        /// <param name="point">直线上某一点</param>
        /// <param name="direct">直线的方向</param>
        /// <param name="planeNormal">垂直于平面的的向量</param>
        /// <param name="planePoint">平面上的任意一点</param>
        /// 与平面交点
        /// RayToPlane(position, dir, Vector3.up, new Vector3(position.x, 0, position.z));
        public static Vector3 RayToPlane(Vector3 point, Vector3 direct, Vector3 planeNormal, Vector3 planePoint)
        {
            var d = Vector3.Dot(planePoint - point, planeNormal) / Vector3.Dot(direct.normalized, planeNormal);
            //print(d);
            return d * direct.normalized + point;
        }

        /// <summary>
        ///     确定坐标是否在多边形内
        /// </summary>
        /// <param name="vecs"></param>
        /// <param name="pos"></param>
        /// <returns></returns>
        public static bool IsInPlane(Vector3[] vecs, Vector3 pos)
        {
            float RadianValue = 0;
            var vecOld = Vector3.zero;
            var vecNew = Vector3.zero;
            for (var i = 0; i < vecs.Length; i++)
            {
                if (i == 0) vecOld = vecs[i] - pos;
                if (i == vecs.Length - 1)
                    vecNew = vecs[0] - pos;
                else
                    vecNew = vecs[i + 1] - pos;
                RadianValue += Mathf.Acos(Vector3.Dot(vecOld.normalized, vecNew.normalized)) * Mathf.Rad2Deg;
                vecOld = vecNew;
            }

            return Mathf.Abs(RadianValue - 360) < 0.1f;
        }

        /// <summary>
        ///     点是否在多边形范围内
        /// </summary>
        /// <param name="p">点</param>
        /// <param name="vertexs">多边形顶点列表</param>
        /// <returns></returns>
        public static bool IsPointInPolygon(Vector2[] vertexs, Vector2 p)
        {
            var crossNum = 0;
            var vertexCount = vertexs.Length;
            for (var i = 0; i < vertexCount; i++)
            {
                var v1 = vertexs[i];
                var v2 = vertexs[(i + 1) % vertexCount];

                if ((v1.y <= p.y && v2.y > p.y)
                    || (v1.y > p.y && v2.y <= p.y))
                    if (p.x < v1.x + (p.y - v1.y) / (v2.y - v1.y) * (v2.x - v1.x))
                        crossNum += 1;
            }

            return crossNum % 2 != 0;
        }

        public static bool IsPointInPolygon(Vector2Int[] vertexs, Vector2 p)
        {
            var crossNum = 0;
            var vertexCount = vertexs.Length;
            for (var i = 0; i < vertexCount; i++)
            {
                Vector2 v1 = vertexs[i];
                Vector2 v2 = vertexs[(i + 1) % vertexCount];

                if ((v1.y <= p.y && v2.y > p.y)
                    || (v1.y > p.y && v2.y <= p.y))
                    if (p.x < v1.x + (p.y - v1.y) / (v2.y - v1.y) * (v2.x - v1.x))
                        crossNum += 1;
            }

            return crossNum % 2 != 0;
        }

        public static bool Point2Shape(Vector2Int sourcePosition, Vector2Int targetPosition, int width, int height)
        {
            var rw = width / 2;
            var rh = height / 2;
            return sourcePosition.x >= targetPosition.x - rw
                   && sourcePosition.x <= targetPosition.x + rw
                   && sourcePosition.y >= targetPosition.y - rh &&
                   sourcePosition.y <= targetPosition.y + rh;
        }

        public static Vector3 LineToLine(Vector3 lineAStart, Vector3 lineAEnd, Vector3 lineBStart,
            Vector3 lineBEnd)
        {
            float x1 = lineAStart.x, y1 = lineAStart.z;
            float x2 = lineAEnd.x, y2 = lineAEnd.z;

            float x3 = lineBStart.x, y3 = lineBStart.z;
            float x4 = lineBEnd.x, y4 = lineBEnd.z;

            //两向量相互垂直，返回0
            if (x1 == x2 && x3 == x4 && x1 == x3) return Vector3.zero;

            //两向量相互平行。返回0
            if (y1 == y2 && y3 == y4 && y1 == y3) return Vector3.zero;

            //两向量相互垂直，返回0
            if (x1 == x2 && x3 == x4) return Vector3.zero;

            //两向量相互平行。返回0
            if (y1 == y2 && y3 == y4) return Vector3.zero;

            float x, y;

            if (x1 == x2)
            {
                var m2 = (y4 - y3) / (x4 - x3);
                var c2 = -m2 * x3 + y3;

                x = x1;
                y = c2 + m2 * x1;
            }
            else if (x3 == x4)
            {
                var m1 = (y2 - y1) / (x2 - x1);
                var c1 = -m1 * x1 + y1;

                x = x3;
                y = c1 + m1 * x3;
            }
            else
            {
                var m1 = (y2 - y1) / (x2 - x1);
                var c1 = -m1 * x1 + y1;
                var m2 = (y4 - y3) / (x4 - x3);
                var c2 = -m2 * x3 + y3;
                x = (c1 - c2) / (m2 - m1);
                y = c2 + m2 * x;
            }

            return new Vector3(x, 0, y);
        }

        public static bool Overlaps(RectInt first, RectInt other)
        {
            return other.xMin <= first.xMax && other.xMax >= first.xMin && other.yMin <= first.yMax &&
                   other.yMax >= first.yMin;
        }

        public static Color ChangeAlpha(Color color, float alpha)
        {
            color.a = alpha;
            return color;
        }
    }
}